/******************************************************/
/****************  请求体响应体校验处理  ****************/
/******************************************************/

const { standTables } = require("../../app/tables-data");
const { getFieldType } = require("../../scripts/note/core/compile/mysql/_utils");
const { emptyVals, needParam, typeOf, consoleLog } = require("../base");

/**
 * 获取参数校验的错误信息
 * @param {object} args 要校验的参数
 * @param {object} rules 校验规则
 */
// exampleRules: [{ required: true, min: 0, max: 10, minLength: 0, maxLength: 10, regexp: /^12$/, validate: () => {} }], //规则示例
function getValidParamsErrMsg(args, rules) {
  let msg = "";
  if (!rules) return msg;
  for (const key in rules) {
    const val = args[key];
    const rule = rules[key];
    const { required, enums, min, max, minLength, maxLength, regexp, validate } = rule;
    if (required && emptyVals.includes(val)) return (msg = `${key}必填`);
    if (min !== undefined && val < min) return (msg = `${key}不能小于${min}`);
    if (max !== undefined && val > max) return (msg = `${key}不能大于${max}`);
    if (enums?.length && !enums.includes(val)) return (msg = `${key}应为${enums.join(", ")}之一`);
    if (regexp !== undefined && !regexp.test(val)) return (msg = `${key}未通过正则校验`);
    if (validate !== undefined) {
      const errTips = validate(val, args);
      if (errTips) msg = errTips;
      return;
    }
    if (minLength !== undefined && val.length < minLength) return (msg = `${key}不能小于${minLength}个字符`);
    if (maxLength !== undefined && val.length > maxLength) return (msg = `${key}不能大于${maxLength}个字符`);
  }
}
/**
 * 判断是否是区间（还有数字区间未考虑到）
 * @param {string} name 字段名称
 * @returns {boolean}
 */
function getIsRange(name = needParam()) {
  return name.includes("time");
}
/**
 * 推断是否采用模糊搜索 【待完善】
 * @param {string} name 字段名称
 * @returns {boolean}
 */
function getInferIsBlur(name = needParam(), tableName) {
  if (!tableName) return;
  const { sqlFields } = standTables[tableName];
  const target = sqlFields.find(it => it.name === name);
  if (!target) return false; // throw new Error(`sql表中未找到该字段：${name}`);
  return getFieldType(target.type) === "string";
}
/**
 * 获取分页参数
 * @param {object} params 参数
 * @returns {object}
 */
function getPageParams(params, tableName, otherParams) {
  const { curr_page = 1, page_size = 20, keyword, ...whereParams } = params;
  Object.keys(whereParams).forEach(key => {
    const val = whereParams[key];
    const t = typeOf(val);
    if (t === "Array") {
      const isRange = getIsRange(key);
      if (isRange) {
        whereParams[`${key}|between`] = val;
        delete whereParams[key];
      }
    } else if (t === "String") {
      if (getInferIsBlur(key, tableName)) {
        whereParams[`${key}|%like%`] = val; // 模糊匹配
        // whereParams[`${key}|/^1.+1$/`] = val; // 这则表达式匹配
        delete whereParams[key];
      }
    }
  });
  if (keyword && otherParams) {
    // if (!otherParams) throw new Error(getResBody({ msg: "未配置 keyword 参数" }));
    const keywordKey = Object.keys(otherParams).find(it => it.startsWith("keyword"));
    if (!keywordKey) throw new Error(getResBody({ msg: "未配置 keyword 参数" }));
    const field = otherParams[keywordKey]; // 只可能是数组或字符串
    const isStr = typeof field === "string";
    const existId = isStr ? field === "id" : field.includes("id");
    const isId = existId && !isNaN(Number(keyword));
    // 如果是id，则根据id值，从高到低位匹配字符串
    const [key, mod = isId ? "like%" : "%like%"] = keywordKey.split("|");
    whereParams[`${isStr ? field : field.join(",")}|${mod}`] = keyword;
  }
  const pageParams = { whereParams, limits: [curr_page - 1, +page_size] };
  if (otherParams?.order) pageParams.order = otherParams.order;
  return pageParams;
}

/**
 * 获取token
 * @param {object} ctx 上下文对象
 * @returns {string}
 */
function getReqToken(ctx) {
  const { token } = getReqParams(ctx);
  if (token) return token;
  return ctx.request.header.token;
}

/**
 * 获取解码后的token信息
 * @param {this} that this
 * @param {string} token token令牌
 * @returns {object}
 */
function getDecodeToken(that, token) {
  const { ctx, app } = that;
  const info = app.config.jwt;
  return ctx.app.jwt.verify(token, info.secret);
}

// 判断是否是空值
function isEmptyVal(val, excludes = ["", undefined, null]) {
  // if (!excludes?.length) return false;
  const t = typeOf(val);
  if (t === "Array") return !val.length;
  if (t === "Object") return Object.keys(val).length === 0;
  return excludes.includes(val);
}

/**
 * 获取请求参数
 * @produce query：url路径里面的参数 request.body： body参数 params：动态路由的参数
 * @param {object} rules 校验规则
 * @param {string[]} ignoreKeys 即使传入，也要忽略的参数
 * @returns {string|undefined} 返回错误信息或undefined
 */
function getReqParams(ctx, rules, ignoreKeys, excludes = ["", null, undefined]) {
  const { query, queries, request, params } = ctx;
  const { method, body, header } = request;
  const args = { ...(method === "GET" ? query : body), ...params };
  // queries 中所有 key 的值，如果存在, 必然是数组类型
  // const rangeKeys = Object.keys(queries).filter(key => queries[key].length === 2 && !Array.isArray(query[key]));
  // rangeKeys.forEach(key => (args[key] = queries[key]));
  Object.keys(queries).forEach(key => {
    const ele = queries[key][0];
    const isObjStr = ele.startsWith("[") || ele.startsWith("{");
    if (isObjStr) args[key] = JSON.parse(ele);
  });
  ignoreKeys?.forEach(key => delete args[key]);
  // 转成数字
  // ['curr_page', 'page_size'].forEach((item) => {
  //     const val = args[item]
  //     if (val !== undefined) args[item] = Number(val)
  // })
  // 排除空值
  if (excludes?.length) {
    for (const key in args) {
      let val = args[key];
      if (isEmptyVal(val, excludes)) delete args[key];
    }
  }
  const errMsg = getValidParamsErrMsg(args, rules);
  if (!errMsg) return args;
  ctx.body = getResBody({ msg: errMsg });
  return;
}

/**
 * 获取响应体（数据）
 * @param {number} code 0 成功，其他值为失败
 * @param {string}  msg  提示信息
 * @param  {any} data  返回的数据信息
 */
function getResBody({ code = 0, msg = "成功", data = null }) {
  // if (data === null) msg = "未找到记录";
  // return { code: msg === "成功" ? code : 1, msg, data };
  if (msg !== "成功") code = 1;
  return { code, msg, data };
}

/**
 * 获取响应体（数据）
 * @param {number} code 0 成功，其他值为失败
 * @param {string}  msg  提示信息
 * @param  {any} data  返回的数据信息
 * @param {number} status 状态码：200 400 500
 * @notice 如果请求 body 超过配置的最大长度，会抛出状态码 413 的异常；body 解析失败（如错误 JSON）会抛出状态码 400 的异常。
 */
function responseData(ctx, { code = 0, msg = "成功", data = null }, status) {
  if (msg !== "成功") code = 1;
  if (status !== undefined) ctx.status = status;
  ctx.body = { code, msg, data };
}

/**
 * 获取返回的列表数据
 * @param {string} tableName 表格名称
 * @param {object} params 参数
 * @param {*} that
 */
function getResList(data, total, limits) {
  const [curr_page, page_size] = limits;
  return {
    records: data,
    total_num: total,
    curr_page: curr_page + 1,
    page_size,
    has_more: curr_page + page_size < total + 1,
  };
}

/**
 * 获取表名称（暂时从url中读取，后续再完善）
 * @param {object} ctx 上下文对象
 * @returns string 表名称
 */
// function getTableName(ctx = needParam()) {
//   return ctx.request.url.split("/")[2];
// }

module.exports = {
  getReqToken,
  getDecodeToken,
  getReqParams,
  getPageParams,
  getResBody,
  responseData,
  getResList,
};
